package com.jzsm.modules.sender.holder;

import cn.hutool.core.lang.Dict;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import com.jzsm.common.annotation.Channel;
import com.jzsm.common.utils.AssertUtils;
import com.jzsm.common.utils.ScmUtil;
import com.jzsm.common.utils.StringUtils;
import com.jzsm.modules.sender.bo.message.MessageBo;
import com.jzsm.modules.sender.pojo.PushChannelAccount;
import com.jzsm.modules.sender.pojo.PushTemplate;
import com.jzsm.modules.sender.dao.PushChannelAccountDao;
import com.jzsm.modules.sender.enums.ChannelTypeEnum;
import com.jzsm.modules.sender.senderService.IMessageSender;
import com.jzsm.modules.sender.senderService.impl.sms.constant.SmsPlaceholderConstant;
import com.jzsm.modules.sender.service.IPushTemplateService;
import com.jzsm.modules.sender.service.impl.PushTemplateServiceImpl;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Component
@FieldDefaults(level = AccessLevel.PRIVATE)
public class PushHolder {
    final Map<String, IMessageSender> channelMap = new HashMap<>();
    final PushChannelAccountDao pushChannelAccountDao;
    final IPushTemplateService pushTemplateService;

    @Autowired
    public PushHolder(
            List<IMessageSender> channels,
            PushChannelAccountDao pushChannelAccountDao,
            PushTemplateServiceImpl pushTemplateService
    ) {
        this.pushChannelAccountDao = pushChannelAccountDao;
        this.pushTemplateService = pushTemplateService;
        channels.forEach(channel -> {
            // 获取原始的目标类；解决AOP后无法获取 注解值
            Class<?> claszz = AopProxyUtils.ultimateTargetClass(channel);
            Channel annotation = claszz.getAnnotation(Channel.class);
            channelMap.put(
                    Objects.nonNull(annotation) ?
                            annotation.value().name() :
                            channel.getClass().getSimpleName(), channel
            );
        });
    }

    /**
     * 推送该消息
     *
     * @param channelType 渠道类型
     * @param message
     */
    public String pushToChannel(String channelType, String message) {
        Long groupId = ScmUtil.getGroupId();
        return pushToChannel(ChannelTypeEnum.valueOf(channelType), StringUtils.toString(groupId), ScmUtil.getOrgId(), message);
    }

    /**
     * 推送该消息
     *
     * @param channelType 渠道类型
     * @param message
     */
    public void pushToChannel(ChannelTypeEnum channelType, String message) {
        IMessageSender pushChannel = channelMap.get(channelType.name());
        if (pushChannel != null) {
            pushChannel.sendMessage(message, null);
        }
    }

    /**
     * 推送该消息 （根据集团id）
     *
     * @param channelType 渠道
     * @param groupId     集团id
     * @param message     消息
     */
    public void pushToChannel(ChannelTypeEnum channelType, String groupId, String message) {
        IMessageSender pushChannel = channelMap.get(channelType.name());
        if (pushChannel != null) {
            pushChannel.sendMessage(message, null);
        }
    }

    /**
     * 推送该消息 （根据集团id & 组织id）
     *
     * @param channelType 渠道类型
     * @param groupId     集团id
     * @param orgId       组织id
     * @param message     消息
     */
    public String pushToChannel(
            ChannelTypeEnum channelType,
            String groupId,
            String orgId,
            String message
    ) {
        IMessageSender pushChannel = channelMap.get(channelType.name());
        AssertUtils.isNotBlank("消息不允许为空！", message);
        AssertUtils.isNotNull("当前渠道不支持！", pushChannel);
        List<PushChannelAccount> accounts = getPushChannelAccounts(channelType, groupId, orgId);
        AssertUtils.isNotEmpty("未找到渠道账号！", accounts);
        PushChannelAccount pushChannelAccount = accounts.get(0);
        return pushChannel.sendMessage(message, pushChannelAccount);
    }

    public String pushToChannel(
            MessageBo messageBo
    ) {
        ChannelTypeEnum channelType = messageBo.getChannelType();
        String message = messageBo.getMessage();
        PushChannelAccount account = messageBo.getAccount();
        IMessageSender pushChannel = channelMap.get(channelType.name());
        AssertUtils.isNotBlank("消息不允许为空！", message);
        AssertUtils.isNotNull("当前渠道不支持！", pushChannel);
        AssertUtils.isNotNull("未找到渠道账号！", account);
        return pushChannel.sendMessage(messageBo);
    }

    /**
     * 撤回消息
     *
     * @param channelType
     * @param message
     * @return
     */
    public String recallMessage(
            String channelType,
            String message
    ) {
        ChannelTypeEnum channelTypeEnum = ChannelTypeEnum.valueOf(channelType);
        IMessageSender pushChannel = channelMap.get(channelTypeEnum.name());
        AssertUtils.isNotBlank("消息不允许为空！", message);
        List<PushChannelAccount> accounts = getPushChannelAccounts(channelTypeEnum, StringUtils.toString(ScmUtil.getGroupId()), ScmUtil.getOrgId());
        PushChannelAccount pushChannelAccount = accounts.get(0);
        return pushChannel.recallMessage(message, pushChannelAccount);
    }

    /**
     * 获取用户
     *
     * @param channelType
     * @param groupId
     * @param orgId
     * @return
     */
    private List<PushChannelAccount> getPushChannelAccounts(ChannelTypeEnum channelType, String groupId, String orgId) {
        PushChannelAccount account = new PushChannelAccount();
        account.setCode(channelType.name()).setPrivacyGroupId(groupId).setOrgId(orgId);
        return pushChannelAccountDao.selectList(account);
    }

    /**
     * 根据模板进行推送
     *
     * @param templateId 模板ID
     * @param businessId 业务方 推送id
     * @param data       请求体
     * @return
     */
    public String pushByTemplateId(String templateId, String businessId, Map<String, Object> data) {
        PushTemplate pushTemplate = pushTemplateService.selectById(templateId);
        AssertUtils.isNotNull("未找到模板！", pushTemplate);
        String chanelAccountId = pushTemplate.getChanelAccountId();
        AssertUtils.isNotBlank("模板的渠道ID不允许为空！", chanelAccountId);
        PushChannelAccount account = pushChannelAccountDao.selectById(chanelAccountId);
        AssertUtils.isNotNull("未找到相关的渠道！", account);
        ChannelTypeEnum channelTypeEnum = ChannelTypeEnum.valueOf(account.getCode());

        // 解析&填充模板
        String message = parseTemplate(pushTemplate.getContent(), account.getCode(), data);

        MessageBo messageBo = new MessageBo()
                .setChannelType(channelTypeEnum)
                .setAccount(account).setBusinessId(businessId)
                .setMessage(message);
        return pushToChannel(messageBo);
    }

    /**
     * 解析模板(当前使用 Beetl)
     * 参考链接 http://ibeetl.com
     *
     * @param templateText 模板文本
     * @param chanelCode   渠道
     * @param data         数据对象
     * @return
     */
    private <T> String parseTemplate(String templateText, String chanelCode, T data) {
        //自动根据用户引入的模板引擎库的jar来自动选择使用的引擎
        //TemplateConfig为模板引擎的选项，可选内容有字符编码、模板路径、模板加载方式等，默认通过模板字符串渲染
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig());

        Dict dict = Dict.create();
        Template template = engine.getTemplate(templateText);

        // map 参数
        if (data instanceof Map) {
            //假设我们引入的是Beetl引擎，则：
            //Dict本质上为Map，此处可用Map
            dict.putAll((Map<? extends String, ?>) data);
        }

        // 数组 参数
        if (data.getClass().isArray()) {
            SmsPlaceholderConstant smsPlaceholderConstant = SmsPlaceholderConstant.valueOf(chanelCode);
            String prefix = "";
            String suffix = "";

            /**
             * sms 占位符填充
             */
            if (Objects.nonNull(smsPlaceholderConstant)) {
                prefix = smsPlaceholderConstant.getPrefix();
                suffix = smsPlaceholderConstant.getSuffix();
            }
            Object[] tempData = (Object[]) data;
            for (int i = 0; i < tempData.length; i++) {
                dict.put(prefix + i + suffix, tempData[i]);
            }
        }
        return template.render(dict);
    }
}
